import { KVNamespace } from "@cloudflare/workers-types";

export type JSONValue = JSONSimpleValue | JSONArray | JSONObject;
export type JSONSimpleValue = number | string | boolean | null;
export type JSONArray = JSONValue[];
export type JSONObject = {
	[name: string]: JSONValue;
};

export class KVAccessor {
	kv: KVNamespace | null = null;
	readonly options: {
		[name: string]: JSONValue;
	} = {};
	readonly data: {
		[name: string]: JSONValue;
	} = {};

	before(kv: KVNamespace) {
		this.kv = kv;
	}
	declare(name: string, defaultValue: JSONValue): void {
		this.options[name] = defaultValue;
	}

	get<T extends JSONValue>(name: string): T {
		return this.data[name] as T;
	}

	async load(name: string): Promise<void> {
		if (!this.kv) throw new Error('no kv found!');
		if (!this.options[name]) throw new Error('option not found!');

		const raw = await this.kv?.get(name, 'text');
		try {
			if (!raw) throw new Error();
			this.data[name] = JSON.parse(raw);
		} catch {
			this.data[name] = this.options[name];
			return;
		}
	}

	async loadAll(): Promise<void> {
		await Promise.all(Object.keys(this.options).map(name => this.load(name)));
	}

	async save(name:string): Promise<void> {
		if (!this.options[name]) throw new Error("no option found!");
		await this.kv?.put(name, JSON.stringify(this.data[name]));
	}

	async saveAll(): Promise<void> {
		await Promise.all(Object.keys(this.options).map(name => this.save(name)));
	}
}
